package com.kh.rationaldrugreview.neo4j.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Session;
import org.neo4j.driver.Result;
import org.neo4j.driver.Transaction;
import org.neo4j.driver.Value;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Neo4j Cypher查询服务
 * 用于执行复杂的图数据库操作
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class Neo4jCypherService {
    
    private final Driver neo4jDriver;
    
    /**
     * 创建或更新实体节点
     * 使用canonicalId作为唯一标识符，确保实体去重
     */
    public Long createOrUpdateEntity(String entityType, String canonicalId, Map<String, Object> properties) {
        // 使用canonicalId作为唯一标识符进行MERGE
        String cypher = String.format(
            "MERGE (n:%s {canonicalId: $canonicalId}) " +
            "SET n += $properties " +
            "RETURN id(n) as nodeId", entityType);
        
        try (Session session = neo4jDriver.session()) {
            return session.writeTransaction(tx -> {
                Map<String, Object> params = new HashMap<>();
                params.put("canonicalId", canonicalId);
                params.put("properties", properties);
                
                Result result = tx.run(cypher, params);
                if (result.hasNext()) {
                    Long nodeId = result.single().get("nodeId").asLong();
                    log.debug("Created/Updated {} node with canonicalId: {}, Neo4j ID: {}", 
                        entityType, canonicalId, nodeId);
                    return nodeId;
                }
                return null;
            });
        }
    }
    
    /**
     * 创建实体间关系
     * 使用canonicalId查找节点，确保关系正确连接到去重后的实体
     */
    public void createRelationship(String sourceCanonicalId, String targetCanonicalId, 
                                  String relationshipType, Map<String, Object> properties) {
        String cypher = String.format(
            "MATCH (source {canonicalId: $sourceCanonicalId}) " +
            "MATCH (target {canonicalId: $targetCanonicalId}) " +
            "MERGE (source)-[r:%s]->(target) " +
            "SET r += $properties", relationshipType);
        
        try (Session session = neo4jDriver.session()) {
            session.writeTransaction(tx -> {
                Map<String, Object> params = new HashMap<>();
                params.put("sourceCanonicalId", sourceCanonicalId);
                params.put("targetCanonicalId", targetCanonicalId);
                params.put("properties", properties);
                
                Result result = tx.run(cypher, params);
                
                // 检查是否成功创建关系
                if (result.consume().counters().relationshipsCreated() > 0) {
                    log.debug("Created {} relationship: {} -> {}", 
                        relationshipType, sourceCanonicalId, targetCanonicalId);
                } else {
                    log.debug("Relationship already exists or nodes not found: {} -> {} [{}]", 
                        sourceCanonicalId, targetCanonicalId, relationshipType);
                }
                
                return null;
            });
        }
    }
    
    /**
     * 批量创建实体
     */
    public void batchCreateEntities(String entityType, List<Map<String, Object>> entities) {
        String cypher = String.format(
            "UNWIND $entities AS entity " +
            "MERGE (n:%s {entityId: entity.entityId}) " +
            "SET n += entity.properties", entityType);
        
        try (Session session = neo4jDriver.session()) {
            session.writeTransaction(tx -> {
                Map<String, Object> params = new HashMap<>();
                params.put("entities", entities);
                tx.run(cypher, params);
                return null;
            });
        }
        
        log.info("Batch created {} {} entities", entities.size(), entityType);
    }
    
    /**
     * 批量创建关系
     */
    public void batchCreateRelationships(String relationshipType, 
                                        List<Map<String, Object>> relationships) {
        String cypher = String.format(
            "UNWIND $relationships AS rel " +
            "MATCH (source {entityId: rel.sourceId}) " +
            "MATCH (target {entityId: rel.targetId}) " +
            "MERGE (source)-[r:%s]->(target) " +
            "SET r += rel.properties", relationshipType);
        
        try (Session session = neo4jDriver.session()) {
            session.writeTransaction(tx -> {
                Map<String, Object> params = new HashMap<>();
                params.put("relationships", relationships);
                tx.run(cypher, params);
                return null;
            });
        }
        
        log.info("Batch created {} {} relationships", relationships.size(), relationshipType);
    }
    
    /**
     * 查询药物相互作用网络
     */
    public List<Map<String, Object>> findDrugInteractionNetwork(String drugName, int depth) {
        String cypher = 
            "MATCH path = (drug:DRUG {name: $drugName})-[:INTERACTS_WITH*1.." + depth + "]-(other:DRUG) " +
            "RETURN drug.name as source, " +
            "       collect(distinct other.name) as interactions, " +
            "       [rel in relationships(path) | rel.severity] as severities";
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = new HashMap<>();
                params.put("drugName", drugName);
                
                Result result = tx.run(cypher, params);
                List<Map<String, Object>> networks = new ArrayList<>();
                
                while (result.hasNext()) {
                    networks.add(result.next().asMap());
                }
                
                return networks;
            });
        }
    }
    
    /**
     * 查询药物的所有关联实体
     */
    public Map<String, List<Map<String, Object>>> findDrugRelatedEntities(String drugName) {
        Map<String, List<Map<String, Object>>> relatedEntities = new HashMap<>();
        
        // 查询治疗的疾病
        String diseasesCypher = 
            "MATCH (drug:DRUG {name: $drugName})-[r:TREATS]->(disease:DISEASE) " +
            "RETURN disease.name as name, disease.icd10Code as code, r.efficacy as efficacy";
        
        // 查询不良反应
        String adverseCypher = 
            "MATCH (drug:DRUG {name: $drugName})-[r:CAUSES]->(ar:ADVERSE_REACTION) " +
            "RETURN ar.name as name, ar.frequency as frequency, ar.severity as severity";
        
        // 查询禁忌症
        String contraCypher = 
            "MATCH (drug:DRUG {name: $drugName})-[r:CONTRAINDICATED_FOR]->(c:CONTRAINDICATION) " +
            "RETURN c.condition as condition, c.type as type, c.reason as reason";
        
        // 查询代谢酶
        String enzymeCypher = 
            "MATCH (drug:DRUG {name: $drugName})-[r:METABOLIZED_BY]->(e:ENZYME) " +
            "RETURN e.name as name, r.pathway as pathway, r.percentage as percentage";
        
        try (Session session = neo4jDriver.session()) {
            // 执行各个查询
            relatedEntities.put("diseases", executeQuery(session, diseasesCypher, drugName));
            relatedEntities.put("adverseReactions", executeQuery(session, adverseCypher, drugName));
            relatedEntities.put("contraindications", executeQuery(session, contraCypher, drugName));
            relatedEntities.put("enzymes", executeQuery(session, enzymeCypher, drugName));
        }
        
        return relatedEntities;
    }
    
    /**
     * 执行查询并返回结果
     */
    private List<Map<String, Object>> executeQuery(Session session, String cypher, String drugName) {
        return session.readTransaction(tx -> {
            Map<String, Object> params = new HashMap<>();
            params.put("drugName", drugName);
            
            Result result = tx.run(cypher, params);
            List<Map<String, Object>> results = new ArrayList<>();
            
            while (result.hasNext()) {
                results.add(result.next().asMap());
            }
            
            return results;
        });
    }
    
    /**
     * 获取知识图谱统计信息
     */
    public Map<String, Object> getGraphStatistics() {
        Map<String, Object> stats = new HashMap<>();
        
        try (Session session = neo4jDriver.session()) {
            // 统计各类节点数量
            String nodeCountCypher = 
                "MATCH (n) " +
                "RETURN labels(n)[0] as type, count(n) as count " +
                "ORDER BY count DESC";
            
            List<Map<String, Object>> nodeCounts = session.readTransaction(tx -> {
                Result result = tx.run(nodeCountCypher);
                List<Map<String, Object>> counts = new ArrayList<>();
                while (result.hasNext()) {
                    counts.add(result.next().asMap());
                }
                return counts;
            });
            
            // 统计关系数量
            String relCountCypher = 
                "MATCH ()-[r]->() " +
                "RETURN type(r) as type, count(r) as count " +
                "ORDER BY count DESC";
            
            List<Map<String, Object>> relCounts = session.readTransaction(tx -> {
                Result result = tx.run(relCountCypher);
                List<Map<String, Object>> counts = new ArrayList<>();
                while (result.hasNext()) {
                    counts.add(result.next().asMap());
                }
                return counts;
            });
            
            stats.put("nodeCounts", nodeCounts);
            stats.put("relationshipCounts", relCounts);
            
            // 总计
            long totalNodes = nodeCounts.stream()
                .mapToLong(m -> (Long) m.get("count"))
                .sum();
            long totalRelationships = relCounts.stream()
                .mapToLong(m -> (Long) m.get("count"))
                .sum();
            
            stats.put("totalNodes", totalNodes);
            stats.put("totalRelationships", totalRelationships);
        }
        
        return stats;
    }
    
    /**
     * 批量查询患者相关风险
     */
    public List<Map<String, Object>> findPatientProfileRisks(List<String> drugNames, 
                                                            String ageGroup, 
                                                            String gender, 
                                                            String pregnancyStatus,
                                                            String renalFunction,
                                                            String hepaticFunction) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})
            OPTIONAL MATCH (drug)-[r1:SUITABLE_FOR|CONTRAINDICATED_FOR_PROFILE|REQUIRES_ADJUSTMENT_FOR]->(pp:PATIENT_PROFILE)
            WHERE ($ageGroup IS NULL OR pp.ageGroup = $ageGroup)
              AND ($gender IS NULL OR pp.gender = $gender OR pp.gender = '不限')
              AND ($pregnancyStatus IS NULL OR pp.pregnancyStatus = $pregnancyStatus)
              AND ($renalFunction IS NULL OR pp.renalFunction = $renalFunction)
              AND ($hepaticFunction IS NULL OR pp.hepaticFunction = $hepaticFunction)
            WITH drug, r1, pp
            RETURN drug.name as drugName,
                   type(r1) as relationshipType,
                   r1 as relationship,
                   pp as patientProfile
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of(
                    "drugNames", drugNames,
                    "ageGroup", ageGroup,
                    "gender", gender,
                    "pregnancyStatus", pregnancyStatus,
                    "renalFunction", renalFunction,
                    "hepaticFunction", hepaticFunction
                );
                
                Result result = tx.run(cypher, params);
                List<Map<String, Object>> results = new ArrayList<>();
                
                while (result.hasNext()) {
                    results.add(result.next().asMap());
                }
                
                return results;
            });
        }
    }
    
    /**
     * 查询所有药物相互作用
     */
    public List<Map<String, Object>> findAllDrugInteractions(List<String> drugNames) {
        String cypher = """
            UNWIND $drugNames as drug1
            UNWIND $drugNames as drug2
            MATCH (d1:DRUG {name: drug1})-[r:INTERACTS_WITH]-(d2:DRUG {name: drug2})
            WHERE drug1 < drug2
            RETURN d1.name as drug1,
                   d2.name as drug2,
                   r.severity as severity,
                   r.mechanism as mechanism,
                   r.clinicalEffect as clinicalEffect,
                   r.management as management,
                   r.strength as strength
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of("drugNames", drugNames);
                Result result = tx.run(cypher, params);
                
                List<Map<String, Object>> interactions = new ArrayList<>();
                while (result.hasNext()) {
                    interactions.add(result.next().asMap());
                }
                
                return interactions;
            });
        }
    }
    
    /**
     * 查询妊娠分级信息
     */
    public List<Map<String, Object>> findPregnancyCategories(List<String> drugNames) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})-[r:HAS_PREGNANCY_CATEGORY]->(pc:PREGNANCY_CATEGORY)
            RETURN drug.name as drugName,
                   pc.category as category,
                   pc.riskLevel as riskLevel,
                   pc.description as description,
                   r.trimesterSpecific as trimesterSpecific,
                   r.fetalRisk as fetalRisk,
                   r.maternalBenefit as maternalBenefit,
                   r.evidenceQuality as evidenceQuality
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of("drugNames", drugNames);
                Result result = tx.run(cypher, params);
                
                List<Map<String, Object>> categories = new ArrayList<>();
                while (result.hasNext()) {
                    categories.add(result.next().asMap());
                }
                
                return categories;
            });
        }
    }
    
    /**
     * 查询患者相关禁忌症
     */
    public List<Map<String, Object>> findContraindicationsForPatient(List<String> drugNames,
                                                                    List<String> patientConditions) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})-[r:CONTRAINDICATED_FOR]->(condition)
            WHERE any(pc in $patientConditions WHERE condition.condition CONTAINS pc OR condition.name CONTAINS pc)
            RETURN drug.name as drugName,
                   condition.condition as condition,
                   condition.name as conditionName,
                   r.type as contraindicationType,
                   r.reason as reason,
                   r.riskLevel as riskLevel,
                   r.alternative as alternative
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of(
                    "drugNames", drugNames,
                    "patientConditions", patientConditions != null ? patientConditions : List.of()
                );
                
                Result result = tx.run(cypher, params);
                List<Map<String, Object>> contraindications = new ArrayList<>();
                
                while (result.hasNext()) {
                    contraindications.add(result.next().asMap());
                }
                
                return contraindications;
            });
        }
    }
    
    /**
     * 查询器官功能要求
     */
    public List<Map<String, Object>> findOrganFunctionRequirements(List<String> drugNames, 
                                                                  String organType) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})-[r:REQUIRES_ORGAN_FUNCTION]->(of:ORGAN_FUNCTION)
            WHERE ($organType IS NULL OR of.organName = $organType)
            RETURN drug.name as drugName,
                   of.organName as organName,
                   of.functionLevel as requiredLevel,
                   of.clinicalMarkers as clinicalMarkers,
                   r.minimumFunction as minimumFunction,
                   r.dosageAdjustment as dosageAdjustment,
                   r.monitoringFrequency as monitoringFrequency,
                   r.contraindicationThreshold as contraindicationThreshold
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of(
                    "drugNames", drugNames,
                    "organType", organType
                );
                
                Result result = tx.run(cypher, params);
                List<Map<String, Object>> requirements = new ArrayList<>();
                
                while (result.hasNext()) {
                    requirements.add(result.next().asMap());
                }
                
                return requirements;
            });
        }
    }
    
    /**
     * 查询年龄组适用性
     */
    public List<Map<String, Object>> findAgeGroupCompatibility(List<String> drugNames, String ageGroup) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})-[r:APPROPRIATE_FOR_AGE]->(ag:AGE_GROUP)
            WHERE ag.groupName = $ageGroup
            RETURN drug.name as drugName,
                   ag.groupName as ageGroup,
                   ag.ageRangeMin as ageRangeMin,
                   ag.ageRangeMax as ageRangeMax,
                   r.dosageByWeight as dosageByWeight,
                   r.dosageByAge as dosageByAge,
                   r.maxDose as maxDose,
                   r.specialConsiderations as specialConsiderations
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of(
                    "drugNames", drugNames,
                    "ageGroup", ageGroup
                );
                
                Result result = tx.run(cypher, params);
                List<Map<String, Object>> compatibility = new ArrayList<>();
                
                while (result.hasNext()) {
                    compatibility.add(result.next().asMap());
                }
                
                return compatibility;
            });
        }
    }
    
    /**
     * 查询不良反应信息
     */
    public List<Map<String, Object>> findAdverseReactions(List<String> drugNames) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})-[r:CAUSES]->(ar:ADVERSE_REACTION)
            RETURN drug.name as drugName,
                   ar.name as adverseReaction,
                   ar.frequency as frequency,
                   ar.severity as severity,
                   ar.bodySystem as bodySystem,
                   r.frequency as relationshipFrequency,
                   r.severity as relationshipSeverity,
                   r.reversibility as reversibility
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of("drugNames", drugNames);
                Result result = tx.run(cypher, params);
                
                List<Map<String, Object>> adverseReactions = new ArrayList<>();
                while (result.hasNext()) {
                    adverseReactions.add(result.next().asMap());
                }
                
                return adverseReactions;
            });
        }
    }
    
    /**
     * 查询代谢酶相关信息
     */
    public List<Map<String, Object>> findMetabolismInfo(List<String> drugNames) {
        String cypher = """
            UNWIND $drugNames as drugName
            MATCH (drug:DRUG {name: drugName})-[r:METABOLIZED_BY|INHIBITS|INDUCES]->(enzyme:ENZYME)
            RETURN drug.name as drugName,
                   enzyme.name as enzymeName,
                   type(r) as relationshipType,
                   r.pathway as pathway,
                   r.percentage as percentage,
                   r.inhibitionType as inhibitionType,
                   r.inductionFold as inductionFold
            """;
        
        try (Session session = neo4jDriver.session()) {
            return session.readTransaction(tx -> {
                Map<String, Object> params = Map.of("drugNames", drugNames);
                Result result = tx.run(cypher, params);
                
                List<Map<String, Object>> metabolism = new ArrayList<>();
                while (result.hasNext()) {
                    metabolism.add(result.next().asMap());
                }
                
                return metabolism;
            });
        }
    }
    
    /**
     * 综合查询药物风险信息
     */
    public Map<String, Object> findComprehensiveRiskData(List<String> drugNames, 
                                                        String ageGroup, 
                                                        String gender,
                                                        String pregnancyStatus,
                                                        String renalFunction,
                                                        String hepaticFunction,
                                                        List<String> patientConditions) {
        Map<String, Object> riskData = new HashMap<>();
        
        // 并行查询各类风险信息
        riskData.put("interactions", findAllDrugInteractions(drugNames));
        riskData.put("pregnancyCategories", findPregnancyCategories(drugNames));
        riskData.put("patientProfiles", findPatientProfileRisks(
            drugNames, ageGroup, gender, pregnancyStatus, renalFunction, hepaticFunction));
        riskData.put("contraindications", findContraindicationsForPatient(drugNames, patientConditions));
        riskData.put("organRequirements", findOrganFunctionRequirements(drugNames, null));
        riskData.put("ageCompatibility", findAgeGroupCompatibility(drugNames, ageGroup));
        riskData.put("adverseReactions", findAdverseReactions(drugNames));
        riskData.put("metabolism", findMetabolismInfo(drugNames));
        
        return riskData;
    }
    
    /**
     * 清空图数据库（慎用）
     */
    public void clearGraph() {
        String cypher = "MATCH (n) DETACH DELETE n";
        
        try (Session session = neo4jDriver.session()) {
            session.writeTransaction(tx -> {
                tx.run(cypher);
                return null;
            });
        }
        
        log.warn("Graph database cleared");
    }
}